using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

namespace Beauty.Net.Middleware
{
    public class GlobalLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<GlobalLoggingMiddleware> _logger;
        private readonly ILogStorage _logStorage;
        private readonly LoggingOptions _options;

        public GlobalLoggingMiddleware(
            RequestDelegate next, 
            ILogger<GlobalLoggingMiddleware> logger,
            ILogStorage logStorage,
            IOptions<LoggingOptions> options)
        {
            _next = next;
            _logger = logger;
            _logStorage = logStorage;
            _options = options.Value;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            // 检查是否需要排除当前路径
            if (ShouldExcludePath(context.Request.Path))
            {
                await _next(context);
                return;
            }

            // 生成唯一的请求ID
            string requestId = context.TraceIdentifier;
            context.Items["RequestId"] = requestId;

            // 记录请求信息
            var stopwatch = Stopwatch.StartNew();
            _logger.LogInformation($"[Request Start] RequestId: {requestId}, Path: {context.Request.Path}, Method: {context.Request.Method}");

            var apiLog = new ApiLog
            {
                RequestId = requestId,
                Path = context.Request.Path,
                Method = context.Request.Method,
                Timestamp = DateTime.UtcNow
            };

            // 读取请求体
            if (_options.LogRequestContent)
            {
                string requestBody = await ReadRequestBodyAsync(context.Request);
                apiLog.RequestBody = TruncateLog(requestBody, _options.MaxLogSize);
                _logger.LogInformation($"[Request Body] RequestId: {requestId}, Body: {apiLog.RequestBody}");
            }

            // 保存原始响应流
            var originalResponseBody = context.Response.Body;
            
            // 创建新的流来捕获响应
            using var responseBody = new MemoryStream();
            context.Response.Body = responseBody;

            try
            {
                // 执行后续中间件
                await _next(context);

                // 记录响应信息
                if (_options.LogResponseContent)
                {
                    responseBody.Seek(0, SeekOrigin.Begin);
                    // 明确指定UTF-8编码，与请求体处理保持一致
                    string responseContent = await new StreamReader(responseBody, encoding: Encoding.UTF8).ReadToEndAsync();
                    responseBody.Seek(0, SeekOrigin.Begin);
                    
                    // 尝试美化JSON格式的响应体，提高可读性
                    string formattedContent = TryFormatJson(responseContent);
                    apiLog.ResponseBody = TruncateLog(formattedContent, _options.MaxLogSize);
                }

                // 复制回原始响应流
                await responseBody.CopyToAsync(originalResponseBody);

                stopwatch.Stop();
                apiLog.StatusCode = context.Response.StatusCode;
                apiLog.ResponseTimeMs = stopwatch.ElapsedMilliseconds;

                _logger.LogInformation($"[Response] RequestId: {requestId}, StatusCode: {apiLog.StatusCode}, ResponseTime: {apiLog.ResponseTimeMs}ms");
            }
            catch (Exception ex)
            {
                // 记录异常信息
                stopwatch.Stop();
                apiLog.StatusCode = 500;
                apiLog.ResponseTimeMs = stopwatch.ElapsedMilliseconds;
                apiLog.ExceptionMessage = ex.Message;
                apiLog.ExceptionStackTrace = ex.StackTrace;

                _logger.LogError(ex, $"[Exception] RequestId: {requestId}, Path: {context.Request.Path}, ResponseTime: {apiLog.ResponseTimeMs}ms");

                // 重新抛出异常以便其他错误处理中间件可以处理
                throw;
            }
            finally
            {
                // 确保恢复原始响应流
                context.Response.Body = originalResponseBody;
                
                // 异步保存日志，不阻塞请求
                try
                {
                    await _logStorage.SaveLogAsync(apiLog);
                }
                catch (Exception ex)
                {
                    // 如果保存日志失败，记录错误但不影响请求处理
                    _logger.LogError(ex, $"Failed to save log to storage for RequestId: {requestId}");
                }
            }
        }

        private bool ShouldExcludePath(PathString path)
        {
            foreach (var excludePath in _options.ExcludePaths)
            {
                if (path.StartsWithSegments(new PathString(excludePath), StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }
            }
            return false;
        }

        private string TryFormatJson(string content)
        {
            try
            {
                // 检查是否可能是JSON格式
                if (!string.IsNullOrEmpty(content) && 
                    (content.TrimStart().StartsWith('{') || content.TrimStart().StartsWith('[')))
                {
                    // 尝试解析JSON以验证格式，但不重新序列化，避免双重转义
                    // 直接返回原始内容，保持JSON的原始格式
                    JsonSerializer.Deserialize<JsonElement>(content);
                    return content;
                }
            }
            catch
            {
                // 如果不是有效的JSON，直接返回原始内容
            }
            return content;
        }

        private string? TruncateLog(string? content, int maxSize)
        {
            if (content == null)
                return null;

            if (content.Length <= maxSize)
                return content;

            return content.Substring(0, maxSize) + "... (truncated)";
        }

        private async Task<string> ReadRequestBodyAsync(HttpRequest request)
        {
            // 确保请求体可以被多次读取
            request.EnableBuffering();
            
            using var reader = new StreamReader(request.Body, encoding: Encoding.UTF8, leaveOpen: true);
            string body = await reader.ReadToEndAsync();
            
            // 重置流位置以便后续中间件可以读取
            request.Body.Seek(0, SeekOrigin.Begin);
            
            return body;
        }
    }
}